{T}

MongoDB 常见问题解答

本文档汇总 MongoDB 开发和运维中的常见问题及解决方案。

连接问题

Q1: 连接超时

症状:客户端无法连接到 MongoDB,出现超时错误。

排查步骤

bash
# 检查 MongoDB 服务状态
sudo systemctl status mongod

# 检查端口监听
netstat -tlnp | grep 27017
# 或
lsof -i :27017

# 检查防火墙
sudo ufw status
sudo ufw allow 27017

# 检查绑定 IP
cat /etc/mongod.conf | grep bindIp

# 测试连接
mongosh "mongodb://localhost:27017"

解决方案

  • 确保 MongoDB 服务正在运行
  • 检查 bindIp 配置,如需远程访问,设置为 0.0.0.0 或具体 IP
  • 开放防火墙端口
  • 检查网络连通性

Q2: 认证失败

症状:连接时报错 Authentication failed

排查步骤

javascript
// 检查用户权限
use admin
db.getUser("myAdmin")

// 查看所有用户
db.getUsers()

解决方案

javascript
// 重置密码
db.changeUserPassword("myAdmin", "newPassword")

// 创建新管理员
db.createUser({
  user: "newAdmin",
  pwd: "password",
  roles: ["root"]
})
bash
# 检查认证数据库
mongosh -u myAdmin -p --authenticationDatabase admin

Q3: 副本集连接问题

症状:无法连接副本集,或连接后无法执行操作。

解决方案

javascript
// 确保连接字符串正确
mongodb://user:pass@host1:27017,host2:27017,host3:27017/db?replicaSet=myRs

// 检查副本集状态
rs.status()

// 检查成员健康状态
rs.isMaster()

// 重新配置副本集(必要时)
rs.reconfig({
  _id: "myRs",
  members: [
    { _id: 0, host: "host1:27017" },
    { _id: 1, host: "host2:27017" },
    { _id: 2, host: "host3:27017" }
  ]
})

性能问题

Q4: 查询慢

症状:查询响应时间长。

排查步骤

javascript
// 1. 使用 explain 分析
db.collection.find({ field: "value" }).explain("executionStats")

// 2. 检查是否使用索引
// 查找 "stage": "COLLSCAN" 表示全表扫描
// "stage": "IXSCAN" 表示使用了索引

// 3. 查看慢查询
db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 })

// 4. 检查索引
db.collection.getIndexes()

解决方案

javascript
// 创建合适索引
db.collection.createIndex({ field: 1 })

// 使用投影
db.collection.find({ field: "value" }, { _id: 0, field: 1 })

// 限制结果
db.collection.find().limit(100)

// 使用覆盖索引
db.collection.createIndex({ status: 1, name: 1 })
db.collection.find({ status: "active" }, { _id: 0, status: 1, name: 1 })

Q5: 内存不足

症状:MongoDB 占用大量内存,或 OOM。

排查步骤

javascript
// 检查内存使用
db.serverStatus().mem

// 检查缓存使用
db.serverStatus().wiredTiger.cache

// 检查连接数
db.serverStatus().connections

解决方案

yaml
# 调整 WiredTiger 缓存
# mongod.conf
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2  # 限制缓存大小
javascript
// 限制查询结果
db.collection.find().limit(100)

// 使用游标迭代
for await (const doc of db.collection.find()) {
  // 处理文档
}

Q6: 写入慢

症状:写入操作耗时过长。

排查步骤

javascript
// 检查写关注级别
db.collection.insertOne({...}, { writeConcern: { w: "majority", j: true } })

// 检查索引数量
db.collection.getIndexes()

// 检查锁情况
db.currentOp({ "waitingForLock": true })

解决方案

javascript
// 批量写入
db.collection.insertMany([...])

// 使用无序写入
db.collection.insertMany([...], { ordered: false })

// 降低写关注(非关键数据)
db.collection.insertOne({...}, { writeConcern: { w: 1 } })

// 减少不必要的索引
db.collection.dropIndex("unused_index")

数据问题

Q7: 文档过大

症状:无法插入文档,报错文档超过 16MB 限制。

解决方案

javascript
// 检查文档大小
Object.bsonsize(db.collection.findOne())

// 方案1:拆分文档
// 将大字段拆分到独立集合

// 方案2:使用 GridFS 存储大文件
const bucket = new mongoose.mongo.GridFSBucket(db)

// 上传文件
fs.createReadStream('./large-file.pdf')
  .pipe(bucket.openUploadStream('large-file.pdf'))

// 下载文件
bucket.openDownloadStreamByName('large-file.pdf')
  .pipe(fs.createWriteStream('./downloaded-file.pdf'))

Q8: 数据类型问题

症状:字段类型不一致导致查询或更新失败。

排查步骤

javascript
// 检查字段类型
db.collection.find({ field: { $type: "string" } })

// 查看所有存在的类型
db.collection.aggregate([
  { $group: { _id: { $type: "$field" } } }
])

解决方案

javascript
// 类型转换(聚合)
db.collection.aggregate([
  { $project: { quantity: { $toInt: "$quantityStr" } } }
])

// 更新字段类型
db.collection.find().forEach(doc => {
  db.collection.updateOne(
    { _id: doc._id },
    { $set: { quantity: parseInt(doc.quantityStr) } }
  )
})

// 使用 $convert 安全转换
db.collection.aggregate([
  {
    $project: {
      quantity: {
        $convert: {
          input: "$quantityStr",
          to: "int",
          onError: 0,
          onNull: 0
        }
      }
    }
  }
])

Q9: 重复键错误

症状:插入时报错 E11000 duplicate key error

排查步骤

javascript
// 检查唯一索引
db.collection.getIndexes()

// 查找重复数据
db.collection.aggregate([
  { $group: { _id: "$email", count: { $sum: 1 }, ids: { $push: "$_id" } } },
  { $match: { count: { $gt: 1 } } }
])

解决方案

javascript
// 删除重复数据(保留第一条)
db.collection.aggregate([
  { $group: { _id: "$email", count: { $sum: 1 }, ids: { $push: "$_id" } } },
  { $match: { count: { $gt: 1 } } }
]).forEach(doc => {
  doc.ids.slice(1).forEach(id => {
    db.collection.deleteOne({ _id: id })
  })
})

// 使用 upsert
db.collection.updateOne(
  { email: "user@example.com" },
  { $set: { name: "User" } },
  { upsert: true }
)

运维问题

Q10: 磁盘空间不足

症状:磁盘使用率过高,无法写入数据。

排查步骤

javascript
// 检查数据库大小
db.stats()

// 检查集合大小
db.collection.stats()

// 查看存储大小
db.collection.stats().storageSize

解决方案

javascript
// 压缩数据(需要维护窗口)
db.runCommand({ compact: "collection" })

// 删除旧数据
db.collection.deleteMany({ createdAt: { $lt: new Date("2023-01-01") } })

// 使用 TTL 索引自动删除
db.collection.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 86400 }  // 24小时后删除
)

// 删除不需要的索引
db.collection.dropIndex("unused_index")
bash
# 修复数据库(需要足够空间)
mongod --repair --dbpath /data/db

Q11: 副本集同步延迟

症状:从节点数据落后于主节点太多。

排查步骤

javascript
// 检查复制延迟
rs.printSlaveReplicationInfo()

// 检查从节点状态
rs.status().members.forEach(m => {
  if (m.stateStr === "SECONDARY") {
    print(m.name + " lag: " + m.optimeDate)
  }
})

解决方案

javascript
// 1. 增加从节点硬件配置(I/O、网络)

// 2. 调整读写关注
db.collection.find().readPref("primary")

// 3. 检查网络延迟
rs.status().members.forEach(m => print(m.name + " ping: " + m.pingMillis + "ms"))

// 4. 禁用链式复制(必要时)
var cfg = rs.conf()
cfg.settings.chainingAllowed = false
rs.reconfig(cfg)

Q12: 分片数据倾斜

症状:部分分片存储的数据远多于其他分片。

排查步骤

javascript
// 检查分片分布
db.collection.getShardDistribution()

// 查看块分布
use config
db.chunks.find({ ns: "mydb.collection" })

// 检查均衡器状态
sh.getBalancerState()

解决方案

javascript
// 1. 选择更好的分片键
// 如果当前分片键不好,需要迁移数据到新集合

// 2. 使用哈希分片
sh.shardCollection("mydb.collection", { field: "hashed" })

// 3. 手动迁移块
sh.moveChunk("mydb.collection", { shardKey: "value" }, "shard2")

// 4. 使用标签分片
sh.addShardTag("shard1", "high")
sh.addTagRange("mydb.collection", { userId: 0 }, { userId: 1000 }, "high")

错误码参考

错误码名称说明解决方案
2BadValue参数错误检查参数格式
6HostNotFound主机未找到检查网络和 DNS
11UserNotFound用户不存在检查用户名和认证库
18AuthenticationFailed认证失败检查用户名密码
48NamespaceExists集合已存在更换集合名或删除后创建
59CommandNotFound命令不存在检查 MongoDB 版本
11000DuplicateKey重复键错误检查唯一索引
11001DuplicateKeyOnUpdate更新时重复键检查更新的值
11600InterruptedAtShutdown关闭时中断重启 MongoDB
13113CursorNotFound游标不存在增加游标超时时间
15998WriteConflict写冲突重试事务
211RangeConflict范围冲突重试操作

性能问题诊断清单

code
□ 检查索引是否被使用
□ 检查是否有全表扫描
□ 检查查询是否返回过多字段
□ 检查是否有 N+1 查询问题
□ 检查写关注级别是否过高
□ 检查连接池配置
□ 检查副本集复制延迟
□ 检查内存使用情况
□ 检查磁盘 I/O
□ 检查网络延迟

上一篇运维管理